package compilers.stmt;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import compilers.Node;
import compilers.condition.Term;
import compilers.node.Identifier;
import compilers.node.Stmt;
import compilers.table.MemScope;
import compilers.types.Type;

public class Var extends Stmt {

	Type fType;
	Vector<Condition> fExpressions = new Vector<Condition>();
	boolean fInit = false;
	int fCount;
	Type fCast = null;
	
	public Var() {}
	
	public Var(Type fType, Identifier fIdentifier, Condition fExpr) {
		super();
		this.setfType(fType.clone());
		this.getfType().setIdentifier(fIdentifier);
		this.getfExpressions().add(fExpr);
		this.startCompute(fExpr);
	}
	
	public Var(Type fType, Identifier fIdentifier, Condition fExpr, Type cast) {
		super();
		this.setfType(fType.clone());
		this.getfType().setIdentifier(fIdentifier);
		this.getfExpressions().add(fExpr);
		this.startCompute(fExpr);
		this.fCast = cast;
	}
	
	public Var(Type fType, Identifier fIdentifier) {
		super();
		this.setfType(fType.clone());
		this.getfType().setIdentifier(fIdentifier);
		if (!fType.isArray()) {
			this.getfExpressions().add(fType.newInit());
		}
	}
	
	public void setInit() {
		fInit = true;
	}
	
	public void initArray() {
		for (int i = this.getfExpressions().size(); i < this.getfType().getArray(); i++) {
			this.getfExpressions().add(getfType().newInit());
		}
	}
	
	public void addExpression(Condition fExpr) {
		this.getfExpressions().add(fExpr);
		this.startCompute(fExpr);
	}
	
	public void startCompute(Condition fExpr) {
		if (fExpr instanceof Type) {
			Type t = (Type)fExpr;
			fType.setfValue(t.getfValue());
			fType.setfValueSet(true);
		} else if (fExpr instanceof Term) {
			Type t = ((Term)fExpr).compute();
			if (t != null) {
				this.getfExpressions().remove(fExpr);
				this.getfExpressions().add(t);
				fType.setfValue(t.getfValue());
				fType.setfValueSet(true);
			}
		}
	}

	@Override
	public void print(int tab) {
		for (int i=1;i<=tab;i++){System.out.print("\t");}System.out.print("|-");
		System.out.println("Var");
		getfType().print(tab+1);
		Enumeration<Condition> enumeration = getfExpressions().elements();
		while(enumeration.hasMoreElements()){
			((Node) enumeration.nextElement()).print(tab+1);
		}  
	}
	
	public void arrayIndices(int index, MemScope scope, Vector<Integer> set) {
		Vector<Integer> ind = fType.getfIdentifier().getIndices(null);
		if ((fCount > 0) && (index < ind.size())) {
			for (int i = 0; i < ind.get(index).intValue(); i++) {
				set.set(index, i);
				arrayIndices((index+1), scope, set);
			}
		} else {
			if (fCount > 0) {
				fCount--;
				char c[] = fType.getfIdentifier().getfIdentifier().toCharArray();
				String n = "";
				for (int i = 0; i < c.length; i++) {
					if (c[i] == '[') {
						n = fType.getfIdentifier().getfIdentifier().substring(0, i);
						break;
					}
				}
				for (int i = 0; i < set.size(); i++) {
					n += "[" + set.get(i).intValue() + "]";
				}
				
				scope.add(n);
			}
		}
	}

	public void generate(BufferedWriter writer, MemScope scope) throws IOException {		
		if (fType.getfIdentifier().getIndices(null).isEmpty()) {
			if (fInit) {
				scope.add(this.getfType().getfIdentifier().getfIdentifier());
			}
			this.getfExpressions().get(0).setfRes(getfType());
			this.getfExpressions().get(0).generate(writer, scope);
			writer.write(this.getfType().str(scope.get(this.getfType().getfIdentifier().getfIdentifier()), scope));
		} else {
			int address = scope.getNextAddress();
			if (scope.get(fType.getfIdentifier().getfIdentifier()) == -1) {
				if (fInit) {
					Vector<Integer> set = new Vector<Integer>();
					Vector<Integer> ind = fType.getfIdentifier().getIndices(null);
					for (int i = 0; i < ind.size(); i++) {
						set.add(0);
					}
					fCount = fExpressions.size();
					arrayIndices(0, scope, set);
					scope.addArray(fType.getfIdentifier().getfIdentifier());
				} else {
					// Toekenning van element in array dat nog niet aan iets is toegekend
					scope.add(this.getfType().getfIdentifier().getfIdentifier());
				}			
			}
			
			for (int i = 0; i < getfExpressions().size(); i++) {
				this.getfExpressions().get(i).setfRes(getfType());
				this.getfExpressions().get(i).generate(writer, scope);
				writer.write(this.getfType().str(i+address, scope));
			}
			
		}
	}

	public Type getfType() {
		return fType;
	}

	public void setfType(Type fType) {
		this.fType = fType;
	}

	public Vector<Condition> getfExpressions() {
		return fExpressions;
	}

	public void setfExpressions(Vector<Condition> fExpressions) {
		this.fExpressions = fExpressions;
	}

}
